﻿using System;
using System.Threading;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using SimpleServiceBus.Bus;
using SimpleServiceBus.Bus.MessageManagement.Callbacks;

namespace SimpleServiceBus.Tests.Bus.MessageManagement.Callbacks
{
    [TestFixture]
    public class CallbackTestFixture
    {
        [Test]
        public void Create_Register_And_Receive_Callback()
        {
            string id = Guid.NewGuid().ToString();
            bool registered = false;
            bool cbExec = false;
            BusAsyncResult argsAr = null;

            var cb = new Callback(id);

            cb.Registered += (sender, args) =>
                                 {
                                     Assert.That(args.MessageId, Is.EqualTo(id));
                                     argsAr = args.Result;
                                     registered = true;
                                 };

            Assert.IsNull(argsAr);

            IAsyncResult ar = cb.Register
                (
                arResult =>
                    {
                        var bar = (BusAsyncResult) arResult;
                        Assert.That(((CompletionResult) bar.AsyncState).State, Is.EqualTo(id));
                        cbExec = true;
                    }, id
                );

            Assert.IsFalse(ar.IsCompleted);
            Assert.IsFalse(cbExec);

            Assert.IsNotNull(argsAr);
            argsAr.Complete(0, null);

            Assert.IsTrue(ar.IsCompleted);
            Assert.IsTrue(cbExec);

            Assert.IsTrue(registered);
        }

        [Test]
        public void Callback_Waits_For_Response_When_Requested()
        {
            var cb = new Callback("tada");
            cb.Registered += (sender, args) =>
                                 {
                                     Action<TimeSpan> wait = WaitForIt;
                                     wait.BeginInvoke(TimeSpan.FromMilliseconds(25), EndWaitForIt, args.Result);
                                 };
            
            var result = cb.WaitForResponse(TimeSpan.FromMilliseconds(50));
            Assert.IsNotNull(result.Message);
            Assert.That(result.Message, Is.EqualTo("Hello"));
           
        }

        [Test]
        public void Callback_Wait_For_Response_Aborts_On_Timeout()
        {
            var cb = new Callback("tada");
            cb.Registered += (sender, args) =>
            {
                Action<TimeSpan> wait = WaitForIt;
                wait.BeginInvoke(TimeSpan.FromMilliseconds(50), EndWaitForIt, args.Result);
            };

            var result = cb.WaitForResponse(TimeSpan.FromMilliseconds(1));
            Assert.IsNull(result.Message);
        }

        private void WaitForIt(TimeSpan timeToWait)
        {
            Thread.Sleep(timeToWait);
        }

        private void EndWaitForIt(IAsyncResult ar)
        {
            var result = (BusAsyncResult)ar.AsyncState;
            result.Complete(0, "Hello");
        }
    }
}